package com.cloudlead.plugin.scheduler;

import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.cloudlead.plugin.scheduler.service.ScheduleJobService;
import com.cloudlead.plugin.scheduler.vo.ScheduleJob;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SchedulerException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import com.cloudlead.utils.scheduler.ScheduledTaskService;
import com.cloudlead.plugin.scheduler.vo.ScheduleJobParams;
import com.cloudlead.plugin.scheduler.vo.dict.ExecType;

/**
 * 
 * @author Dean
 *
 */
public class DefaultExecuteJob implements Job {

	@Override
	public void execute(JobExecutionContext context)
			throws JobExecutionException {
		JobDataMap jobDataMap = context.getMergedJobDataMap();
		// 注入spring容器
		ApplicationContext applicationContext;
		try {
			applicationContext = (ApplicationContext) context.getScheduler()
					.getContext().get(ScheduledTaskService.SPRING_CONTEXT_KEY);
		} catch (SchedulerException e1) {
			e1.printStackTrace();
			throw new JobExecutionException(e1);
		}
		ScheduleJob jobTask = (ScheduleJob) jobDataMap
				.get(ScheduleJobService.SCHEDULE_JOB_PARAM);
		ExecType execType = jobTask.getExecType();
		String exec = jobTask.getExec();
		if (null != exec && exec.length() > 0) {
			if (ExecType.java_class.equals(execType)) {
				int lastIndexOf = exec.lastIndexOf(".");
				if (lastIndexOf <= 0) {
					throw new JobExecutionException(
							"Job task exec params not valid.");
				}
				String className = exec.substring(0, lastIndexOf);
				String methodName = exec.substring(lastIndexOf + 1);
				try {
					Class<?> jobExecutionClass = (Class<?>) Class
							.forName(className);
					Object jobExecution = jobExecutionClass.newInstance();
					if (jobExecution instanceof ApplicationContextAware) {
						ApplicationContextAware applicationContextAware = (ApplicationContextAware) jobExecution;
						applicationContextAware
								.setApplicationContext(applicationContext);
					}
					if (jobExecution instanceof JobExecutionContextAware) {
						JobExecutionContextAware jobExecutionContextAware = (JobExecutionContextAware) jobExecution;
						jobExecutionContextAware
								.setJobExecutionContext(context);
					}
					invokeJobExecutionMethod(jobTask, methodName, jobExecution);
				} catch (InstantiationException | IllegalAccessException
						| ClassNotFoundException e) {
					e.printStackTrace();
					throw new JobExecutionException(e);
				}
			} else if (ExecType.spring_bean.equals(execType)) {
				String[] split = exec.split("\\.");
				if (split.length != 2) {
					throw new JobExecutionException(
							"Job task exec params not valid.");
				}
				String beanName = split[0];
				String methodName = split[1];
				Object jobExecution = applicationContext.getBean(beanName);
				if (jobExecution instanceof JobExecutionContextAware) {
					JobExecutionContextAware jobExecutionContextAware = (JobExecutionContextAware) jobExecution;
					jobExecutionContextAware.setJobExecutionContext(context);
				}
				invokeJobExecutionMethod(jobTask, methodName, jobExecution);
			}
		}
	}

	private void invokeJobExecutionMethod(ScheduleJob jobTask,
			String methodName, Object bean) throws JobExecutionException {
		try {
			if (null == jobTask.getScheduleJobParamss()
					|| jobTask.getScheduleJobParamss().isEmpty()) {
				MethodUtils.invokeMethod(bean, methodName);
			} else {
				List<Object> params = new ArrayList<Object>();
				List<ScheduleJobParams> scheduleJobParams = jobTask
						.getScheduleJobParamss();
				Collections.sort(scheduleJobParams);
				for (ScheduleJobParams scheduleJobParam : scheduleJobParams) {
					Class<?> targetType = String.class;
					switch (scheduleJobParam.getParamType()) {
					case str:
						targetType = String.class;
						break;
					case integer:
						targetType = Integer.class;
						break;
					case decimal:
						targetType = BigDecimal.class;
						break;
					case bool:
						targetType = Boolean.class;
						break;
					default:
						targetType = String.class;
						break;
					}
					params.add(ConvertUtils.convert(
							scheduleJobParam.getParamValue(), targetType));
				}
				MethodUtils.invokeMethod(bean, methodName, params.toArray());
			}
		} catch (NoSuchMethodException | IllegalAccessException e) {
			e.printStackTrace();
			throw new JobExecutionException(e);
		} catch (InvocationTargetException e) {
			e.printStackTrace();
			throw new JobExecutionException(e.getTargetException());
		}
	}
}
